home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 25 / AACD 25.iso / AACD / Magazine / Online / QMail / source / qmail-local.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-10-17  |  18.0 KB  |  710 lines

  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include "readwrite.h"
  4. #include "sig.h"
  5. #include "env.h"
  6. #include "byte.h"
  7. #include "exit.h"
  8. #include "fork.h"
  9. #include "open.h"
  10. #include "wait.h"
  11. #include "lock.h"
  12. #include "seek.h"
  13. #include "substdio.h"
  14. #include "getln.h"
  15. #include "subfd.h"
  16. #include "sgetopt.h"
  17. #include "alloc.h"
  18. #include "error.h"
  19. #include "stralloc.h"
  20. #include "fmt.h"
  21. #include "str.h"
  22. #include "now.h"
  23. #include "case.h"
  24. #include "quote.h"
  25. #include "qmail.h"
  26. #include "slurpclose.h"
  27. #include "myctime.h"
  28. #include "gfrom.h"
  29. #include "auto_patrn.h"
  30.  
  31. #ifdef __amigaos__
  32. #include <sys/file.h>
  33. #include <string.h>
  34. #endif
  35.  
  36. void err(s) char *s; { substdio_putsflush(subfderr,s); }
  37. void soft() { _exit(111); }
  38. void hard() { _exit(100); }
  39.  
  40. void temp_childcrashed() { err("Aack, child crashed. (#4.3.0)\n"); soft(); }
  41. void temp_rewind() { err("Unable to rewind message. (#4.3.0)\n"); soft(); }
  42. void temp_fork() { err("Unable to fork. (#4.3.0)\n"); soft(); }
  43. void temp_read() { err("Error while reading message. (#4.3.0)\n"); soft(); }
  44. void temp_write() { err("Error while writing message. (#4.3.0)\n"); soft(); }
  45. void temp_child() { err("Temporary error in forwarding message. (#4.3.0)\n"); soft(); }
  46. void temp_maildirtimeout() { err("Timeout on maildir delivery. (#4.3.0)\n"); soft(); }
  47. void temp_maildir() { err("Temporary error on maildir delivery. (#4.3.0)\n"); soft(); }
  48. void temp_nomaildir() { err("Unable to chdir to maildir. (#4.2.1)\n"); soft(); }
  49. void temp_open(fn) char *fn; { err("Unable to open "); err(fn); err(". (#4.2.1)\n"); soft(); }
  50.  
  51. void temp_blankline() { err("Uh-oh: first line of .qmail file is blank. (#4.2.1)\n"); soft(); }
  52. void temp_fofile() { err("Uh-oh: .qmail has file delivery but has e bit set. (#4.7.0)\n"); soft(); }
  53. void temp_foprog() { err("Uh-oh: .qmail has prog delivery but has e bit set. (#4.7.0)\n"); soft(); }
  54. void temp_nomem() { err("Out of memory. (#4.3.0)\n"); soft(); }
  55. void temp_chdir() { err("Unable to switch to home directory. (#4.3.0)\n"); soft(); }
  56. void temp_homestat() { err("Unable to stat home directory. (#4.3.0)\n"); soft(); }
  57. void temp_homesticky() { err("Home directory has h bit set: user is editing his .qmail file. (#4.2.1)\n"); soft(); }
  58. void temp_homewritable() { err("Uh-oh: home directory is group or world writable. (#4.7.0)\n"); soft(); }
  59. void temp_qmwritable() { err("Uh-oh: .qmail file is group or world writable. (#4.7.0)\n"); soft(); }
  60. void temp_nfsqmail() { err("Temporary error trying to open .qmail file. (#4.3.0)\n"); soft(); }
  61. void temp_denyqmail() { err("Permission error trying to open .qmail file. (#4.3.0)\n"); soft(); }
  62. void temp_slowlock() { err("File has been locked for 30 seconds straight. (#4.3.0)\n"); soft(); }
  63.  
  64. void bounce_childperm() { err("Permanent error in forwarding message. (#5.2.4)\n"); hard(); }
  65. void bounce_loop() { err("This message is looping: it already has my Delivered-To line. (#5.4.6)\n"); hard(); }
  66. void bounce_ext() { err("Sorry, no mailbox here by that name. (#5.1.1)\n"); hard(); }
  67. void usage() { err("qmail-local: usage: qmail-local [ -nN ] user homedir local dash ext domain sender aliasempty\n"); hard(); }
  68.  
  69. void warn_homesticky() { err("Warning: home directory has h bit set.\n"); }
  70.  
  71. int flagdoit;
  72. int flag99;
  73.  
  74. char *user;
  75. char *homedir;
  76. char *local;
  77. char *dash;
  78. char *ext;
  79. char *host;
  80. char *sender;
  81. char *aliasempty;
  82.  
  83. stralloc dashext = {0};
  84. stralloc ufline = {0};
  85. stralloc rpline = {0};
  86. stralloc envrecip = {0};
  87. stralloc dtline = {0};
  88. stralloc qme = {0};
  89. stralloc ueo = {0};
  90. stralloc cmds = {0};
  91. stralloc messline = {0};
  92. stralloc foo = {0};
  93.  
  94. char buf[1024];
  95. char outbuf[1024];
  96.  
  97. /* child process */
  98.  
  99. char fntmptph[80 + FMT_ULONG * 2];
  100. char fnnewtph[80 + FMT_ULONG * 2];
  101. void tryunlinktmp() { unlink(fntmptph); }
  102. void sigalrm() { tryunlinktmp(); _exit(3); }
  103.  
  104. void maildir_child(dir)
  105. char *dir;
  106. {
  107.  unsigned long pid;
  108.  unsigned long time;
  109.  char host[64];
  110.  char *s;
  111.  int loop;
  112.  struct stat st;
  113.  int fd;
  114.  substdio ss;
  115.  substdio ssout;
  116.  
  117.  sig_alarmcatch(sigalrm);
  118.  if (chdir(dir) == -1) { if (error_temp(errno)) _exit(1); _exit(2); }
  119.  pid = getpid();
  120.  host[0] = 0;
  121.  gethostname(host,sizeof(host));
  122.  for (loop = 0;;++loop)
  123.   {
  124.    time = now();
  125.    s = fntmptph;
  126.    s += fmt_str(s,"tmp/");
  127.    s += fmt_ulong(s,time); *s++ = '.';
  128.    s += fmt_ulong(s,pid); *s++ = '.';
  129.    s += fmt_strn(s,host,sizeof(host)); *s++ = 0;
  130.    if (stat(fntmptph,&st) == -1) if (errno == error_noent) break;
  131.    /* really should never get to this point */
  132.    if (loop == 2) _exit(1);
  133.    sleep(2);
  134.   }
  135.  str_copy(fnnewtph,fntmptph);
  136.  byte_copy(fnnewtph,3,"new");
  137.  
  138.  alarm(86400);
  139.  fd = open_excl(fntmptph);
  140.  if (fd == -1) _exit(1);
  141.  
  142.  substdio_fdbuf(&ss,read,0,buf,sizeof(buf));
  143.  substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
  144.  if (substdio_put(&ssout,rpline.s,rpline.len) == -1) goto fail;
  145.  if (substdio_put(&ssout,dtline.s,dtline.len) == -1) goto fail;
  146.  
  147.  switch(substdio_copy(&ssout,&ss))
  148.   {
  149.    case -2: tryunlinktmp(); _exit(4);
  150.    case -3: goto fail;
  151.   }
  152.  
  153.  if (substdio_flush(&ssout) == -1) goto fail;
  154.  if (fsync(fd) == -1) goto fail;
  155.  if (close(fd) == -1) goto fail; /* NFS dorks */
  156.  
  157.  if (link(fntmptph,fnnewtph) == -1) goto fail;
  158.    /* if it was error_exist, almost certainly successful; i hate NFS */
  159.  tryunlinktmp(); _exit(0);
  160.  
  161.  fail: tryunlinktmp(); _exit(1);
  162. }
  163.  
  164. /* end child process */
  165.  
  166. void maildir(fn)
  167. char *fn;
  168. {
  169.  int child;
  170.  int wstat;
  171.  
  172.  if (seek_begin(0) == -1) temp_rewind();
  173.  
  174.  switch(child = fork())
  175.   {
  176.    case -1:
  177.      temp_fork();
  178.    case 0:
  179.      maildir_child(fn);
  180.      soft();
  181.   }
  182.  
  183.  wait_pid(&wstat,child);
  184.  if (wait_crashed(wstat))
  185.    temp_childcrashed();
  186.  switch(wait_exitcode(wstat))
  187.   {
  188.    case 0: break;
  189.    case 2: temp_nomaildir();
  190.    case 3: temp_maildirtimeout();
  191.    case 4: temp_read();
  192.    default: temp_maildir();
  193.   }
  194. }
  195.  
  196. void slowlock() { temp_slowlock(); }
  197.  
  198. void mailfile(fn)
  199. char *fn;
  200. {
  201.  int fd;
  202.  substdio ss;
  203.  substdio ssout;
  204.  int match;
  205.  seek_pos pos;
  206.  int flaglocked;
  207.  
  208.  if (seek_begin(0) == -1) temp_rewind();
  209.  
  210. #ifndef __amigaos__
  211.  fd = open_append(fn);
  212.  if (fd == -1) temp_open(fn);
  213.  
  214.  sig_alarmcatch(slowlock);
  215.  alarm(30);
  216.  flaglocked = (lock_ex(fd) != -1);
  217.  alarm(0);
  218.  sig_alarmdefault();
  219.  
  220. #else /* __amigaos__ */
  221.  /* Attempt a mandatory exclusive lock for upto 30 seconds. */
  222.  {
  223.    int c;
  224.  
  225.    for (c = 30, flaglocked = 0; !flaglocked && c >= 0; c--)
  226.    {
  227.      if (-1 != (fd = open_append(fn)))
  228.      {
  229.        if (!(flaglocked = (-1 != amiga_flock (fd, LOCK_EX | LOCK_NB))))
  230.          close (fd);
  231.      }
  232.      /* If the file couldn't be locked, sleep for a second unless this
  233.       * was the final attempt.
  234.       */
  235.      if (!flaglocked && c != 0)
  236.        sleep (1);
  237.    }
  238.    if (!flaglocked)
  239.      temp_slowlock();
  240.  }
  241. #endif /* __amigaos__ */
  242.  
  243.  seek_end(fd);
  244.  pos = seek_cur(fd);
  245.  
  246.  substdio_fdbuf(&ss,read,0,buf,sizeof(buf));
  247.  substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
  248.  if (substdio_put(&ssout,ufline.s,ufline.len)) goto writeerrs;
  249.  if (substdio_put(&ssout,rpline.s,rpline.len)) goto writeerrs;
  250.  if (substdio_put(&ssout,dtline.s,dtline.len)) goto writeerrs;
  251.  for (;;)
  252.   {
  253.    if (getln(&ss,&messline,&match,'\n') != 0) 
  254.     { if (flaglocked) seek_trunc(fd,pos); close(fd); temp_read(); }
  255.    if (!match && !messline.len) break;
  256.    if (gfrom(messline.s,messline.len))
  257.      if (substdio_bput(&ssout,">",1)) goto writeerrs;
  258.    if (substdio_bput(&ssout,messline.s,messline.len)) goto writeerrs;
  259.    if (!match)
  260.     {
  261.      if (substdio_bputs(&ssout,"\n")) goto writeerrs;
  262.      break;
  263.     }
  264.   }
  265.  if (substdio_bputs(&ssout,"\n")) goto writeerrs;
  266.  if (substdio_flush(&ssout)) goto writeerrs;
  267.  if (fsync(fd) == -1) goto writeerrs;
  268.  close(fd);
  269.  return;
  270.  
  271.  writeerrs:
  272.  if (flaglocked) seek_trunc(fd,pos);
  273.  close(fd);
  274.  temp_write();
  275. }
  276.  
  277. void mailprogram(prog)
  278. char *prog;
  279. {
  280.  int child;
  281.  char *(args[4]);
  282.  int wstat;
  283.  
  284.  if (seek_begin(0) == -1) temp_rewind();
  285.  
  286.  switch(child = fork())
  287.   {
  288.    case -1:
  289.      temp_fork();
  290.    case 0:
  291.      args[0] = "sh"; args[1] = "-c"; args[2] = prog; args[3] = 0;
  292.      sig_pipedefault();
  293.      execvp(*args,args);
  294.      if (errno == error_txtbsy) { err("Text busy. (#4.3.0)\n"); soft(); }
  295.      if (errno == error_nomem) { err("Out of memory. (#4.3.0)\n"); soft(); }
  296.      if (errno == error_io) { err("I/O error. (#4.3.0)\n"); soft(); }
  297.      if (error_temp(errno)) { err("Temporary error. (#4.3.0)\n"); soft(); }
  298.      err("Unable to execute "); err(*args); err(" (#5.2.4)\n");
  299.      hard();
  300.   }
  301.  
  302.  wait_pid(&wstat,child);
  303.  if (wait_crashed(wstat))
  304.    temp_childcrashed();
  305.  switch(wait_exitcode(wstat))
  306.   {
  307.    case 100:
  308.    case 64: case 65: case 70: case 76: case 77: case 78: case 112: hard();
  309.    case 0: break;
  310.    case 99: flag99 = 1; break;
  311.    default: soft();
  312.   }
  313. }
  314.  
  315. unsigned long mailforward_qp = 0;
  316.  
  317. void mailforward(recips)
  318. char **recips;
  319. {
  320.  struct qmail qqt;
  321.  substdio ss;
  322.  int match;
  323.  
  324.  if (seek_begin(0) == -1) temp_rewind();
  325.  substdio_fdbuf(&ss,read,0,buf,sizeof(buf));
  326.  
  327.  if (qmail_open(&qqt) == -1) temp_fork();
  328.  mailforward_qp = qmail_qp(&qqt);
  329.  qmail_put(&qqt,dtline.s,dtline.len);
  330.  do
  331.   {
  332.    if (getln(&ss,&messline,&match,'\n') != 0) { qmail_fail(&qqt); break; }
  333.    qmail_put(&qqt,messline.s,messline.len);
  334.   }
  335.  while (match);
  336.  qmail_from(&qqt,ueo.s);
  337.  while (*recips) qmail_to(&qqt,*recips++);
  338.  switch(qmail_close(&qqt))
  339.   {
  340.    case QMAIL_TOOLONG: bounce_childperm();
  341.    case QMAIL_READ: temp_read();
  342.    case 0: return;
  343.    default: temp_child();
  344.   }
  345. }
  346.  
  347. void bouncexf()
  348. {
  349.  int match;
  350.  substdio ss;
  351.  
  352.  if (seek_begin(0) == -1) temp_rewind();
  353.  substdio_fdbuf(&ss,read,0,buf,sizeof(buf));
  354.  for (;;)
  355.   {
  356.    if (getln(&ss,&messline,&match,'\n') != 0) temp_read();
  357.    if (!match) break;
  358.    if (messline.len <= 1)
  359.      break;
  360.    if (messline.len == dtline.len)
  361.      if (!str_diffn(messline.s,dtline.s,dtline.len))
  362.        bounce_loop();
  363.   }
  364. }
  365.  
  366. void checkhome()
  367. {
  368.  struct stat st;
  369.  
  370.  if (stat(".",&st) == -1) temp_homestat();
  371.  if (st.st_mode & auto_patrn) temp_homewritable();
  372.  if (st.st_mode & 01000)
  373.    if (flagdoit) temp_homesticky(); else warn_homesticky();
  374. }
  375.  
  376. int qmeox(dashowner)
  377. char *dashowner;
  378. {
  379.  struct stat st;
  380.  
  381.  if (!stralloc_copys(&qme,".qmail")) temp_nomem();
  382.  if (!stralloc_cat(&qme,&dashext)) temp_nomem();
  383.  if (!stralloc_cats(&qme,dashowner)) temp_nomem();
  384.  if (!stralloc_0(&qme)) temp_nomem();
  385.  if (stat(qme.s,&st) == -1)
  386.   {
  387.    if (error_temp(errno)) temp_nfsqmail();
  388.    return -1;
  389.   }
  390.  return 0;
  391. }
  392.  
  393. int qmeopen(cutable)
  394. int *cutable;
  395. {
  396.  int fd;
  397.  struct stat st;
  398.  int i;
  399.  
  400.  i = dashext.len;
  401.  for (;;)
  402.   {
  403.    if (!stralloc_copys(&qme,".qmail")) temp_nomem();
  404.    if (!stralloc_catb(&qme,dashext.s,i)) temp_nomem();
  405.    if (i < dashext.len) if (!stralloc_cats(&qme,"-default")) temp_nomem();
  406.    if (!stralloc_0(&qme)) temp_nomem();
  407.    fd = open_read(qme.s);
  408.    if (fd == -1)
  409.     {
  410.      if (error_temp(errno)) temp_nfsqmail();
  411.      if (errno == error_perm) temp_denyqmail();
  412.      if (errno == error_acces) temp_denyqmail();
  413.     }
  414.    else
  415.     {
  416.      if (fstat(fd,&st) == -1) temp_nfsqmail();
  417.      if ((st.st_mode & S_IFMT) == S_IFREG)
  418.       {
  419.        if (st.st_mode & auto_patrn) temp_qmwritable();
  420.        *cutable = !!(st.st_mode & 0100);
  421.        return fd;
  422.       }
  423.      close(fd);
  424.     }
  425.    if (!i) return -1;
  426.    do
  427.      if (dashext.s[--i] == '-') break;
  428.    while (i);
  429.   }
  430. }
  431.  
  432. unsigned long count_file = 0;
  433. unsigned long count_forward = 0;
  434. unsigned long count_program = 0;
  435. char count_buf[FMT_ULONG];
  436.  
  437. void count_print()
  438. {
  439.  substdio_puts(subfdoutsmall,"did ");
  440.  substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,count_file));
  441.  substdio_puts(subfdoutsmall,"+");
  442.  substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,count_forward));
  443.  substdio_puts(subfdoutsmall,"+");
  444.  substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,count_program));
  445.  substdio_puts(subfdoutsmall,"\n");
  446.  if (mailforward_qp)
  447.   {
  448.    substdio_puts(subfdoutsmall,"qp ");
  449.    substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,mailforward_qp));
  450.    substdio_puts(subfdoutsmall,"\n");
  451.   }
  452.  substdio_flush(subfdoutsmall);
  453. }
  454.  
  455. void sayit(type,cmd,len)
  456. char *type;
  457. char *cmd;
  458. int len;
  459. {
  460.  substdio_puts(subfdoutsmall,type);
  461.  substdio_put(subfdoutsmall,cmd,len);
  462.  substdio_putsflush(subfdoutsmall,"\n");
  463. }
  464.  
  465. void main(argc,argv)
  466. int argc;
  467. char **argv;
  468. {
  469.  int opt;
  470.  int i;
  471.  int j;
  472.  int k;
  473.  int fd;
  474.  int numforward;
  475.  char **recips;
  476.  datetime_sec starttime;
  477.  int flagforwardonly;
  478.  char *extx;
  479.  
  480.  umask(077);
  481.  sig_pipeignore();
  482.  
  483.  if (!env_init()) temp_nomem();
  484.  
  485.  flagdoit = 1;
  486.  while ((opt = getopt(argc,argv,"nN")) != opteof)
  487.    switch(opt)
  488.     {
  489.      case 'n': flagdoit = 0; break;
  490.      case 'N': flagdoit = 1; break;
  491.      case '?':
  492.      default:
  493.        hard();
  494.     }
  495.  argc -= optind;
  496.  argv += optind;
  497.  
  498.  if (!(user = *argv++)) usage();
  499.  if (!(homedir = *argv++)) usage();
  500.  if (!(local = *argv++)) usage();
  501.  if (!(dash = *argv++)) usage();
  502.  if (!(ext = *argv++)) usage();
  503.  if (!(host = *argv++)) usage();
  504.  if (!(sender = *argv++)) usage();
  505.  if (!(aliasempty = *argv++)) usage();
  506.  if (*argv) usage();
  507.  
  508. #ifdef __amigaos__
  509.  if (strchr (homedir, ':') <= &homedir[0]) usage();
  510. #else
  511.  if (homedir[0] != '/') usage();
  512. #endif
  513.  if (chdir(homedir) == -1) temp_chdir();
  514.  checkhome();
  515.  
  516.  if (!env_put2("HOST",host)) temp_nomem();
  517.  if (!env_put2("HOME",homedir)) temp_nomem();
  518.  if (!env_put2("USER",user)) temp_nomem();
  519.  if (!env_put2("LOCAL",local)) temp_nomem();
  520.  
  521.  if (!stralloc_copys(&envrecip,local)) temp_nomem();
  522.  if (!stralloc_cats(&envrecip,"@")) temp_nomem();
  523.  if (!stralloc_cats(&envrecip,host)) temp_nomem();
  524.  
  525.  if (!stralloc_copy(&foo,&envrecip)) temp_nomem();
  526.  if (!stralloc_0(&foo)) temp_nomem();
  527.  if (!env_put2("RECIPIENT",foo.s)) temp_nomem();
  528.  
  529.  if (!stralloc_copys(&dtline,"Delivered-To: ")) temp_nomem();
  530.  if (!stralloc_cat(&dtline,&envrecip)) temp_nomem();
  531.  for (i = 0;i < dtline.len;++i) if (dtline.s[i] == '\n') dtline.s[i] = '_';
  532.  if (!stralloc_cats(&dtline,"\n")) temp_nomem();
  533.  
  534.  if (!stralloc_copy(&foo,&dtline)) temp_nomem();
  535.  if (!stralloc_0(&foo)) temp_nomem();
  536.  if (!env_put2("DTLINE",foo.s)) temp_nomem();
  537.  
  538.  if (flagdoit)
  539.    bouncexf();
  540.  
  541.  if (!env_put2("SENDER",sender)) temp_nomem();
  542.  
  543.  if (!quote2(&foo,sender)) temp_nomem();
  544.  if (!stralloc_copys(&rpline,"Return-Path: <")) temp_nomem();
  545.  if (!stralloc_cat(&rpline,&foo)) temp_nomem();
  546.  for (i = 0;i < rpline.len;++i) if (rpline.s[i] == '\n') rpline.s[i] = '_';
  547.  if (!stralloc_cats(&rpline,">\n")) temp_nomem();
  548.  
  549.  if (!stralloc_copy(&foo,&rpline)) temp_nomem();
  550.  if (!stralloc_0(&foo)) temp_nomem();
  551.  if (!env_put2("RPLINE",foo.s)) temp_nomem();
  552.  
  553.  if (!stralloc_copys(&ufline,"From ")) temp_nomem();
  554.  if (*sender)
  555.   {
  556.    int len; int i; char ch;
  557.  
  558.    len = str_len(sender);
  559.    if (!stralloc_readyplus(&ufline,len)) temp_nomem();
  560.    for (i = 0;i < len;++i)
  561.     {
  562.      ch = sender[i];
  563.      if ((ch == ' ') || (ch == '\t') || (ch == '\n')) ch = '-';
  564.      ufline.s[ufline.len + i] = ch;
  565.     }
  566.    ufline.len += len;
  567.   }
  568.  else
  569.    if (!stralloc_cats(&ufline,"MAILER-DAEMON")) temp_nomem();
  570.  if (!stralloc_cats(&ufline," ")) temp_nomem();
  571.  starttime = now();
  572.  if (!stralloc_cats(&ufline,myctime(starttime))) temp_nomem();
  573.  
  574.  if (!stralloc_copy(&foo,&ufline)) temp_nomem();
  575.  if (!stralloc_0(&foo)) temp_nomem();
  576.  if (!env_put2("UFLINE",foo.s)) temp_nomem();
  577.  
  578.  if (!stralloc_copys(&dashext,dash)) temp_nomem();
  579.  if (!stralloc_cats(&dashext,ext)) temp_nomem();
  580.  for (i = 0;i < dashext.len;++i)
  581. #ifdef __amigaos__
  582.    if (dashext.s[i] == '.' || dashext.s[i] == ':' || dashext.s[i] == '/')
  583.      dashext.s[i] = ';';
  584. #else
  585.    if (dashext.s[i] == '.')
  586.      dashext.s[i] = ':';
  587. #endif
  588.  case_lowerb(dashext.s,dashext.len);
  589.  
  590.  extx = ext;
  591.  if (!env_put2("EXT",extx)) temp_nomem();
  592.  extx += str_chr(extx,'-'); if (*extx) ++extx;
  593.  if (!env_put2("EXT2",extx)) temp_nomem();
  594.  extx += str_chr(extx,'-'); if (*extx) ++extx;
  595.  if (!env_put2("EXT3",extx)) temp_nomem();
  596.  extx += str_chr(extx,'-'); if (*extx) ++extx;
  597.  if (!env_put2("EXT4",extx)) temp_nomem();
  598.  
  599.  flagforwardonly = 0;
  600.  fd = qmeopen(&flagforwardonly);
  601.  if (fd == -1) if (*dash) bounce_ext();
  602.  
  603.  if (!stralloc_copys(&ueo,sender)) temp_nomem();
  604.  if (str_diff(sender,""))
  605.    if (str_diff(sender,"#@[]"))
  606.      if (qmeox("-owner") == 0)
  607.       {
  608.        if (qmeox("-owner-default") == 0)
  609.     {
  610.          if (!stralloc_copys(&ueo,local)) temp_nomem();
  611.          if (!stralloc_cats(&ueo,"-owner-@")) temp_nomem();
  612.          if (!stralloc_cats(&ueo,host)) temp_nomem();
  613.          if (!stralloc_cats(&ueo,"-@[]")) temp_nomem();
  614.     }
  615.        else
  616.     {
  617.          if (!stralloc_copys(&ueo,local)) temp_nomem();
  618.          if (!stralloc_cats(&ueo,"-owner@")) temp_nomem();
  619.          if (!stralloc_cats(&ueo,host)) temp_nomem();
  620.     }
  621.       }
  622.  if (!stralloc_0(&ueo)) temp_nomem();
  623.  if (!env_put2("NEWSENDER",ueo.s)) temp_nomem();
  624.  
  625.  if (!stralloc_ready(&cmds,0)) temp_nomem();
  626.  cmds.len = 0;
  627.  if (fd != -1)
  628.    if (slurpclose(fd,&cmds,256) == -1) temp_nomem();
  629.  
  630.  if (!cmds.len)
  631.   {
  632.    if (!stralloc_copys(&cmds,aliasempty)) temp_nomem();
  633.    flagforwardonly = 0;
  634.   }
  635.  if (!cmds.len || (cmds.s[cmds.len - 1] != '\n'))
  636.    if (!stralloc_cats(&cmds,"\n")) temp_nomem();
  637.  
  638.  numforward = 0;
  639.  i = 0;
  640.  for (j = 0;j < cmds.len;++j)
  641.    if (cmds.s[j] == '\n')
  642.     {
  643.      switch(cmds.s[i]) { case '&': ++numforward; break;
  644.        default: break; }
  645.      i = j + 1;
  646.     }
  647.  
  648.  recips = (char **) alloc((numforward + 1) * sizeof(char *));
  649.  if (!recips) temp_nomem();
  650.  numforward = 0;
  651.  
  652.  flag99 = 0;
  653.  
  654.  i = 0;
  655.  for (j = 0;j < cmds.len;++j)
  656.    if (cmds.s[j] == '\n')
  657.     {
  658.      cmds.s[j] = 0;
  659.      k = j;
  660.      while ((k > i) && (cmds.s[k - 1] == ' ') || (cmds.s[k - 1] == '\t'))
  661.        cmds.s[--k] = 0;
  662.      switch(cmds.s[i])
  663.       {
  664.        case 0: /* k == i */
  665.      if (i) break;
  666.      temp_blankline();
  667.        case '#':
  668.          break;
  669.        case '|':
  670.      ++count_program;
  671.      if (flagforwardonly) temp_foprog();
  672.          if (flagdoit) mailprogram(cmds.s + i + 1);
  673.          else sayit("program ",cmds.s + i + 1,k - i - 1);
  674.          break;
  675.        case '+':
  676.      if (str_equal(cmds.s + i + 1,"list"))
  677.        flagforwardonly = 1;
  678.      break;
  679.        case '&':
  680.          ++i;
  681.      ++count_forward;
  682.          if (flagdoit) recips[numforward++] = cmds.s + i;
  683.          else sayit("forward ",cmds.s + i,k - i);
  684.          break;
  685.        default:
  686.      ++count_file;
  687.      if (flagforwardonly) temp_fofile();
  688.      if (cmds.s[k - 1] == '/')
  689.            if (flagdoit) maildir(cmds.s + i);
  690.            else sayit("maildir ",cmds.s + i,k - i);
  691.      else
  692.            if (flagdoit) mailfile(cmds.s + i);
  693.            else sayit("mbox ",cmds.s + i,k - i);
  694.          break;
  695.  
  696.       }
  697.      i = j + 1;
  698.      if (flag99) break;
  699.     }
  700.  
  701.  if (numforward) if (flagdoit)
  702.   {
  703.    recips[numforward] = 0;
  704.    mailforward(recips);
  705.   }
  706.  
  707.  count_print();
  708.  _exit(0);
  709. }
  710.